setwd("/shared/ifbstor1/projects/dubii2021/djarrige/m3-stat-R/mini-projet")| libraries |
|---|
| knitr |
| FactoMineR |
| factoextra |
| gprofiler2 |
| pheatmap |
| dplyr |
| ClassDiscovery |
| biomaRt |
Le but de ce travail est de mettre en oeuvre les méthodes vues dans le module 3 “R et statistiques” pour explorer le jeu de données de Pavkovic, et de rendre un rapport d’analyse au format .Rmd.
Nous fournissons ci-dessous une trame avec les principales sections attendues. Certaines contiennent déjà du code. Vous devrez en compléter d’autres. Sentez-vous libres d’adapter cette trame ou d’y ajouter des analyses complémentaires si elles vous aident à interpréter vos résultats.
Date: le 26 mai 2021 à minuit. Si vous anticipez un problème pour remettre le rapport à cette date contactez-nous aussi rapidement que possible pour que nous puissions prévoir une remise plus tardive.
Commencez par renommer le fichier .Rmd en remplaçant Prenom-NOM par vos nom et prénom.
Le rapport est attendu en formats .Rmd + .HTML (en gardant l’option self_contained de l’en-tête activée).
Déposez les fichiers dans un sous-dossier de vote compte du cluster. Attention, veillez à respecter précisément cette structure de chemin car nous nous baserons dessus pour récupérer vos résultats.
/shared/projects/dubii2021/[login]/m3-stat-R/mini-projet
Nous vous encourageons à assurer la lisibilité de votre code (syntaxe, nommage des variables, commentaires de code)
Nous partons du même jeu de données Fil Rouge de ce module issues de la publication Pavkovic, M., Pantano, L., Gerlach, C.V. et al. Multi omics analysis of fibrotic kidneys in two mouse models. Sci Data 6, 92 (2019). https://doi.org/10.1038/s41597-019-0095-5
Rappel sur les échantillons:
Deux modèles de fibrose rénale chez la souris sont étudiés:
Le premier est un modèle de néphropathie réversible induite par l’acide folique (folic acid (FA)). Les souris ont été sacrifiées avant le traitement (normal), puis à jour 1, 2, 7 et 14 (day1,…) après une seule injection d’acide folique.
Le second est un modèle irréversible induit chrirurgicalement (unilateral ureteral obstruction (UUO)). les souris ont été sacrifiées avant obstruction (day 0) et à 3, 7 et 14 jours après obstruction par ligation de l’uretère du rein gauche.
A partir de ces extraits de rein, l’ARN messager total et les petits ARNs ont été séquencés et les protéines caratérisées par spectrométrie de masse en tandem (TMT).
But scientifique: Dans le tutoriel sur les dataframes, vous avez travaillé sur les données de transcriptome du modèle UUO. Dans ce mini-projet, vous allez travailler sur les données du transcriptome du modèle FA afin de regrouper les observations (échantillon) et les gènes selon des profils d’expression similaires.
Votre projet se décompose en 5 parties dont 3 seront à réaliser par vous:
Vous n’avez rien à coder ici. Le code est fourni.
Le bloc suivant contient une fonction qui permet de télécharger un fichier dans l’espace de travail, sauf s’il est déjà présent. Nous l’utiliserons ensuite pour télécharger les données à analyser en évitant de refaire le transfert à chaque exécution de l’analyse.
#' @title Download a file only if it is not yet here
#' @author Jacques van Helden email{Jacques.van-Helden@@france-bioinformatique.fr}
#' @param url_base base of the URL, that will be prepended to the file name
#' @param file_name name of the file (should not contain any path)
#' @param local_folder path of a local folder where the file should be stored
#' @return the function returns the path of the local file, built from local_folder and file_name
#' @export©
download_only_once <- function(
url_base,
file_name,
local_folder) {
## Define the source URL
url <- file.path(url_base, file_name)
message("Source URL\n\t", url)
## Define the local file
local_file <- file.path(local_folder, file_name)
## Create the local data folder if it does not exist
dir.create(local_folder, showWarnings = FALSE, recursive = TRUE)
## Download the file ONLY if it is not already there
if (!file.exists(local_file)) {
message("Downloading file from source URL to local file\n\t",
local_file)
download.file(url = url, destfile = local_file)
} else {
message("Local file already exists, no need to download\n\t",
local_file)
}
return(local_file)
}Nous téléchargeons deux fichiers dans un dossier local ~/m3-stat-R/pavkovic_analysis (vous pouvez changer le nom ou chemin dans le chunk ci-dessous), et les chargeons dans les data.frames suivants:
fa_expr_rawfa_meta## Define the remote URL and local folder
pavkovic_url <- "https://github.com/DU-Bii/module-3-Stat-R/raw/master/stat-R_2021/data/pavkovic_2019/"
## Define the local folder for this analysis (where the data will be downloaded and the results generated)
pavkovic_folder <- "~/m3-stat-R/pavkovic_analysis"
## Define a sub-folder for the data
pavkovic_data_folder <- file.path(pavkovic_folder, "data")
## Download and load the expression data table
## Note: we use check.names=FALSE to avoid replacing hyphens by dots
## in sample names, because we want to keep them as in the
## original data files.
message("Downloading FA transcriptome file\t", "fa_raw_counts.tsv.gz",
"\n\tfrom\t", pavkovic_url)
fa_expr_file <- download_only_once(
url_base = pavkovic_url,
file_name = "fa_raw_counts.tsv.gz",
local_folder = pavkovic_data_folder)
## Load the expresdsion table
message("Loading FA transcriptome data from\n\t", fa_expr_file)
fa_expr_raw <- read.delim(file = fa_expr_file,
header = TRUE,
row.names = 1)
## Download the metadata file
message("Downloading FA metadata file\t", "fa_transcriptome_metadata.tsv",
"\n\tfrom\t", pavkovic_url)
fa_meta_file <- download_only_once(
url_base = pavkovic_url,
file_name = "fa_transcriptome_metadata.tsv",
local_folder = pavkovic_data_folder)
## Load the metadata
message("Loading FA metadata from\n\t", fa_meta_file)
fa_meta <- read.delim(file = fa_meta_file,
header = TRUE,
row.names = 1)Nous regardons la structure de chaque dataframe.
str(fa_expr_raw)'data.frame': 46679 obs. of 18 variables:
$ day1_1 : num 2278.8 0 36.3 13.2 0 ...
$ day1_2 : num 1786.5 0 22.15 7.15 27.9 ...
$ day1_3 : num 2368.62 0 39.48 1.12 6.9 ...
$ day14_1 : num 627.758 0 14.471 0.867 5.692 ...
$ day14_2 : num 559.2 0 10.2 0 1.9 ...
$ day14_3 : num 611.434 0 31.691 0 0.655 ...
$ day2_1 : num 2145.22 0 300.56 1.71 57.38 ...
$ day2_2 : num 262.45 0 4.77 0 0 ...
$ day2_3 : num 745.84 0 123.9 5.26 38.9 ...
$ day3_1 : num 987.185 0 51.856 0.802 8.931 ...
$ day3_2 : num 1077.65 0 8.43 0 6.97 ...
$ day3_3 : num 1335.1 0 69.9 0 0 ...
$ day7_1 : num 1096.08 0 6.67 0 7.94 ...
$ day7_2 : num 1035.846 0 6.955 0.849 101.648 ...
$ day7_3 : num 1090.04 0 42.58 1.71 0.65 ...
$ normal_1: num 483.23 0 7.35 0.86 32.06 ...
$ normal_2: num 1842.1 0 11.2 0 10.4 ...
$ normal_3: num 475.7 0 1.03 0 0 ...
str(fa_meta)'data.frame': 18 obs. of 5 variables:
$ dataType : chr "transcriptome" "transcriptome" "transcriptome" "transcriptome" ...
$ sampleName : chr "day14_1" "day14_2" "day14_3" "day1_1" ...
$ condition : chr "day14" "day14" "day14" "day1" ...
$ sampleNumber: int 1 2 3 1 2 3 1 2 3 1 ...
$ color : chr "#FF4400" "#FF4400" "#FF4400" "#BBD7FF" ...
Les deux fichiers ne donnent pas les observations de l’échantillon dans le même ordre:
fa_meta$sampleName == names(fa_expr_raw) [1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Nous les réorganisons les échantillons dans l’ordre de l’expérience: condition normale, puis day 1 à 14 avec les 3 réplicats.
sample_order <- c(paste(rep(c("normal", "day1", "day2", "day3", "day7", "day14"), each = 3),
1:3, sep = "_"))
fa_expr_raw <- fa_expr_raw[,sample_order]
fa_meta <- fa_meta[match(sample_order, fa_meta$sampleName),]
# View(fa_meta)
kable(fa_meta, caption = "Metdata for Pavkovoc FA transcriptome")| dataType | sampleName | condition | sampleNumber | color | |
|---|---|---|---|---|---|
| 16 | transcriptome | normal_1 | normal | 1 | #BBFFBB |
| 17 | transcriptome | normal_2 | normal | 2 | #BBFFBB |
| 18 | transcriptome | normal_3 | normal | 3 | #BBFFBB |
| 4 | transcriptome | day1_1 | day1 | 1 | #BBD7FF |
| 5 | transcriptome | day1_2 | day1 | 2 | #BBD7FF |
| 6 | transcriptome | day1_3 | day1 | 3 | #BBD7FF |
| 7 | transcriptome | day2_1 | day2 | 1 | #F0BBFF |
| 8 | transcriptome | day2_2 | day2 | 2 | #F0BBFF |
| 9 | transcriptome | day2_3 | day2 | 3 | #F0BBFF |
| 10 | transcriptome | day3_1 | day3 | 1 | #FFFFDD |
| 11 | transcriptome | day3_2 | day3 | 2 | #FFFFDD |
| 12 | transcriptome | day3_3 | day3 | 3 | #FFFFDD |
| 13 | transcriptome | day7_1 | day7 | 1 | #FFDD88 |
| 14 | transcriptome | day7_2 | day7 | 2 | #FFDD88 |
| 15 | transcriptome | day7_3 | day7 | 3 | #FFDD88 |
| 1 | transcriptome | day14_1 | day14 | 1 | #FF4400 |
| 2 | transcriptome | day14_2 | day14 | 2 | #FF4400 |
| 3 | transcriptome | day14_3 | day14 | 3 | #FF4400 |
=> Ainsi, nous avons un jeu de données avec un échantillon de 18 observations et des données d’expression de 46679 gènes.
Dans le tutorial sur les dataframes sur le jeu de données “uuo” (relisez le corrigé), nous vous avons demandé de créer un data.frame qui collecte les statistiques par gène et par échantillon. Nous vous demandons de réaliser une étude similaire sur les données “FA” avant et après normalisation inter-échantillons des données. Le code de la partie avant normalisation est donné.
Nous créons un data.frame nommé sample_stat_prenorm qui comporte une ligne par échantillon et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 d’expression de chaque échantillon:
Il est affiché avec la fonction kable().
message("Computing sample-wise statistics on raw counts")
sample_stat_prenorm <- data.frame(
mean = apply(fa_expr_raw, 2, mean, na.rm = TRUE),
sd = apply(fa_expr_raw, 2, sd, na.rm = TRUE),
iqr = apply(fa_expr_raw, 2, IQR, na.rm = TRUE),
Q1 = apply(fa_expr_raw, 2, quantile, p = 0.25, na.rm = TRUE),
median = apply(fa_expr_raw, 2, median, na.rm = TRUE),
Q3 = apply(fa_expr_raw, 2, quantile, p = 0.75, na.rm = TRUE),
max = apply(fa_expr_raw, 2, max, na.rm = TRUE),
null = apply(fa_expr_raw == 0, 2, sum, na.rm = TRUE)
)
kable(sample_stat_prenorm, caption = "Sample-wise statistics before normalisation.")| mean | sd | iqr | Q1 | median | Q3 | max | null | |
|---|---|---|---|---|---|---|---|---|
| normal_1 | 576.2992 | 57643.81 | 46.38417 | 0 | 1.166694 | 46.38417 | 11777395 | 21415 |
| normal_2 | 1172.5615 | 111509.82 | 103.13749 | 0 | 2.975840 | 103.13749 | 22658521 | 20203 |
| normal_3 | 531.8620 | 57203.96 | 41.80724 | 0 | 1.081680 | 41.80724 | 11636735 | 21660 |
| day1_1 | 663.0580 | 65219.36 | 58.76393 | 0 | 1.105540 | 58.76393 | 13174979 | 21700 |
| day1_2 | 1224.5149 | 123140.72 | 111.71166 | 0 | 3.053561 | 111.71166 | 25118595 | 20152 |
| day1_3 | 1368.6144 | 133450.04 | 122.38187 | 0 | 3.766666 | 122.38187 | 27255572 | 19621 |
| day2_1 | 1161.7628 | 103598.87 | 127.04744 | 0 | 3.501480 | 127.04744 | 21036079 | 19978 |
| day2_2 | 223.8844 | 20431.97 | 14.54242 | 0 | 0.000000 | 14.54242 | 4037963 | 24446 |
| day2_3 | 628.4406 | 57480.66 | 63.36112 | 0 | 1.478198 | 63.36112 | 11662570 | 21455 |
| day3_1 | 649.8389 | 55210.88 | 77.84371 | 0 | 2.076066 | 77.84371 | 11170233 | 20772 |
| day3_2 | 514.8649 | 42768.34 | 74.16479 | 0 | 1.941705 | 74.16479 | 8612320 | 21142 |
| day3_3 | 854.0997 | 80565.29 | 104.95124 | 0 | 2.975772 | 104.95124 | 16484449 | 20287 |
| day7_1 | 576.2437 | 53112.82 | 90.51017 | 0 | 2.256164 | 90.51017 | 10690493 | 20865 |
| day7_2 | 510.2741 | 47373.18 | 80.57478 | 0 | 2.537673 | 80.57478 | 9682527 | 20467 |
| day7_3 | 604.9701 | 59072.56 | 83.50019 | 0 | 2.874706 | 83.50019 | 12164986 | 20045 |
| day14_1 | 540.2640 | 54481.09 | 65.01270 | 0 | 2.025707 | 65.01270 | 11178198 | 20496 |
| day14_2 | 681.9987 | 74825.35 | 69.05744 | 0 | 2.032894 | 69.05744 | 15430799 | 20870 |
| day14_3 | 562.5697 | 56026.62 | 75.11681 | 0 | 2.247271 | 75.11681 | 11575718 | 20396 |
Nous créons ci-dessous un data.frame nommé gene_stat_prenorm qui comporte une ligne par gène et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 de chaque gène.
Ces résultats sont stockés dans un data.frame avec 1 ligne par échantillon et 1 colonne par statistique. Nous affichons les lignes 100 à 109 de ce tableau de statistiques avec la fonction kable().
## Gene-wise statistics for the raw counts (will be used for normalisation)
message("Computing gene-wise statistics on raw counts")
gene_stat_prenorm <- data.frame(
mean = apply(fa_expr_raw, 1, mean, na.rm = TRUE),
sd = apply(fa_expr_raw, 1, sd, na.rm = TRUE),
iqr = apply(fa_expr_raw, 1, IQR, na.rm = TRUE),
Q1 = apply(fa_expr_raw, 1, quantile, p = 0.25, na.rm = TRUE),
median = apply(fa_expr_raw, 1, median, na.rm = TRUE),
Q3 = apply(fa_expr_raw, 1, quantile, p = 0.75, na.rm = TRUE),
max = apply(fa_expr_raw, 1, max, na.rm = TRUE),
null = apply(fa_expr_raw == 0, 1, sum, na.rm = TRUE)
)
kable(gene_stat_prenorm[100:109, ], caption = "Gene-wise statistics before normalisation")| mean | sd | iqr | Q1 | median | Q3 | max | null | |
|---|---|---|---|---|---|---|---|---|
| ENSMUSG00000000567 | 320.336744 | 286.399084 | 364.362298 | 91.046357 | 261.659973 | 455.40866 | 1052.68945 | 0 |
| ENSMUSG00000000568 | 857.348825 | 694.432189 | 282.978949 | 575.571199 | 711.096105 | 858.55015 | 3406.45030 | 0 |
| ENSMUSG00000000579 | 422.075702 | 162.290415 | 157.732606 | 340.417017 | 447.572742 | 498.14962 | 728.51737 | 0 |
| ENSMUSG00000000581 | 498.978227 | 248.612029 | 259.324338 | 320.599085 | 471.269224 | 579.92342 | 942.51104 | 0 |
| ENSMUSG00000000594 | 2113.558334 | 1237.521694 | 937.571105 | 1400.226182 | 1868.718762 | 2337.79729 | 6408.96044 | 0 |
| ENSMUSG00000000600 | 2299.978163 | 911.222891 | 1480.192582 | 1719.874980 | 2093.572929 | 3200.06756 | 3674.18811 | 0 |
| ENSMUSG00000000605 | 325.552118 | 169.456624 | 195.879561 | 221.735904 | 282.608205 | 417.61546 | 791.31256 | 0 |
| ENSMUSG00000000606 | 9.929258 | 32.151350 | 2.736779 | 1.178311 | 1.934744 | 3.91509 | 138.48677 | 4 |
| ENSMUSG00000000617 | 18.804116 | 9.470819 | 13.654701 | 12.188276 | 18.150391 | 25.84298 | 36.00235 | 0 |
| ENSMUSG00000000627 | 15.623306 | 14.341852 | 12.308127 | 4.780913 | 10.064895 | 17.08904 | 46.29752 | 0 |
Vous n’avez rien à coder ici. Le code est fourni.
Il existe plusieurs façons de normaliser les données de transcriptome vues dans les modules 4 et 5 (cf. total counts, quantiles, TMM, RLE, limma voom,…), mais nous avons choisi ici une solution simple tout en étant robuste pour normaliser les données en standardisant le 3ème quantile.
La méthode choisie ici consiste à :
Ecarter les gènes “non-détectés”, c’est-à-dire ceux ayant des valeurs nulles dans au moins 90% des échantillons.
Ecarter les gènes à peine exprimés, c’est-à-dire ceux ayant une valeur moyenne < 10 (arbitrairement).
Standardiser les échantillons sur le 3ème quartile des gènes restants: on divise les comptages bruts par le 3ème quartile de l’échantillon et on multiplie par le 3ème quartile de l’ensemble des échantillons.
Normaliser les comptages (au sens propre, c’est-à-dire rapprocher leur distribution de la distribution gaussienne) par une transformation logarithmique (log2).
Nous fournissons ci-dessous le code.
## Data filtering: genes having at least 90% null values
message("Filtering undetected genes")
undetected_genes <- gene_stat_prenorm$null >= ncol(fa_expr_raw) * 0.9
print(paste0("Undetected genes (null in >= 90% samples): ", sum(undetected_genes)))[1] "Undetected genes (null in >= 90% samples): 14380"
## Data filtering: genes having a mean expression < 10
message("Filtering barely expressed genes")
barely_expressed_genes <- gene_stat_prenorm$mean < 10
print(paste0("Barely expressed genes (mean < 10): ", sum(barely_expressed_genes)))[1] "Barely expressed genes (mean < 10): 26286"
## Apply filtering on both criteria
discarded_genes <- undetected_genes | barely_expressed_genes
print(paste0("Discarded genes: ", sum(discarded_genes)))[1] "Discarded genes: 26288"
kept_genes <- !discarded_genes
print(paste0("Kept genes: ", sum(kept_genes)))[1] "Kept genes: 20391"
## Genes after filtering
fa_expr_filtered <- fa_expr_raw[kept_genes, ]Nous appliquons ici une méthode simple mais efficace de standardisation en appliquant un facteur multiplicatif qui ramène tous les échantillons au même troisième quartile (\(Q3\)).
#### Inter-sample standardisation on the Q3 of raw counts ####
total_q3 <- quantile(unlist(fa_expr_filtered), probs = 0.75)
sample_stat_prenorm$Q3_filterd <- apply(fa_expr_filtered, 2, quantile, probs = 0.75)
sample_stat_prenorm$scale_factor <- 1 / sample_stat_prenorm$Q3_filterd * total_q3
## Apply standardisation
fa_expr_standard <- t(t(fa_expr_filtered) * unlist(sample_stat_prenorm$scale_factor))
## Check 3rd quantile after standardisation
kable(apply(fa_expr_standard, 2, quantile, probs = 0.75),
col.names = "Q3_standardised",
caption = "Third quartile of the filtered counts after inter-sample standardisation of the third quartiles. ")| Q3_standardised | |
|---|---|
| normal_1 | 364.9332 |
| normal_2 | 364.9332 |
| normal_3 | 364.9332 |
| day1_1 | 364.9332 |
| day1_2 | 364.9332 |
| day1_3 | 364.9332 |
| day2_1 | 364.9332 |
| day2_2 | 364.9332 |
| day2_3 | 364.9332 |
| day3_1 | 364.9332 |
| day3_2 | 364.9332 |
| day3_3 | 364.9332 |
| day7_1 | 364.9332 |
| day7_2 | 364.9332 |
| day7_3 | 364.9332 |
| day14_1 | 364.9332 |
| day14_2 | 364.9332 |
| day14_3 | 364.9332 |
#boxplot(fa_expr_standard, horizontal = TRUE)Nous appliquons une transformation en log2 des données brutes, après avoir ajouté un epsilon \(\epsilon = 1\) (les valeurs nulles seront donc représentées par un log2(counts) valant \(0\). Nous stockons le résultat dans un data.frame nommé fa_expr_log2.
Nous affichons un fragment des tableaux fa_expr_raw et fa_expr_log2 en sélectionnant les lignes 100 à 109 et les colonnes 5 à 10, afin de nous assurer que la transformation en log2 a bien fonctionné.
## Log2 transformation of the transcriptome data
epsilon <- 1
fa_expr_log2 <- log2(fa_expr_standard + epsilon)
# dim(fa_expr_log2)
# View(head(fa_expr_log2))
## Display of a fragment of the data before and after log2 transformation
kable(fa_expr_raw[100:109, 5:10], caption = "Fragment des données transcriptomiques brutes")| day1_2 | day1_3 | day2_1 | day2_2 | day2_3 | day3_1 | |
|---|---|---|---|---|---|---|
| ENSMUSG00000000567 | 599.648075 | 304.093177 | 1052.689447 | 106.995584 | 347.13842 | 479.59911 |
| ENSMUSG00000000568 | 1008.026306 | 1349.126716 | 818.257157 | 116.136417 | 3406.45030 | 766.09722 |
| ENSMUSG00000000579 | 599.832586 | 500.735597 | 473.399472 | 36.258005 | 410.57787 | 347.05054 |
| ENSMUSG00000000581 | 942.511039 | 744.646735 | 546.260344 | 87.788535 | 319.12679 | 461.45732 |
| ENSMUSG00000000594 | 3063.845705 | 2743.404374 | 2283.051270 | 1115.588250 | 1491.61384 | 1576.68451 |
| ENSMUSG00000000600 | 3341.854530 | 3561.805180 | 3674.188108 | 589.840068 | 1399.10751 | 3446.01856 |
| ENSMUSG00000000605 | 791.312561 | 558.710943 | 489.579657 | 77.008213 | 256.00200 | 282.80077 |
| ENSMUSG00000000606 | 4.785252 | 6.566374 | 3.970399 | 0.000000 | 138.48677 | 0.00000 |
| ENSMUSG00000000617 | 15.839486 | 29.138902 | 30.228506 | 6.670500 | 18.27493 | 13.41152 |
| ENSMUSG00000000627 | 36.673777 | 35.967747 | 46.297517 | 2.878275 | 17.03638 | 15.45854 |
kable(fa_expr_log2[100:109, 5:10], caption = "Fragment des données transcriptomiques après transformation log2")| day1_2 | day1_3 | day2_1 | day2_2 | day2_3 | day3_1 | |
|---|---|---|---|---|---|---|
| ENSMUSG00000000711 | 9.709367 | 9.861557 | 9.287726 | 12.335149 | 9.755002 | 10.013232 |
| ENSMUSG00000000724 | 5.904466 | 6.218882 | 4.464461 | 5.782181 | 5.701477 | 5.803482 |
| ENSMUSG00000000731 | 5.203571 | 5.404039 | 4.963615 | 5.970908 | 4.299021 | 4.505637 |
| ENSMUSG00000000732 | 5.398925 | 8.209062 | 2.126857 | 3.751910 | 6.948597 | 4.970987 |
| ENSMUSG00000000738 | 8.520431 | 8.430289 | 8.263532 | 7.909466 | 8.250468 | 7.455809 |
| ENSMUSG00000000740 | 11.228789 | 11.320488 | 11.117466 | 10.630164 | 11.595716 | 10.156658 |
| ENSMUSG00000000743 | 7.882486 | 7.268069 | 8.646853 | 10.469226 | 6.135917 | 7.385994 |
| ENSMUSG00000000751 | 7.302103 | 8.404643 | 7.851161 | 6.677037 | 8.189071 | 7.123055 |
| ENSMUSG00000000753 | 0.000000 | 2.885227 | 2.310527 | 0.000000 | 0.000000 | 4.605696 |
| ENSMUSG00000000759 | 8.296064 | 8.620921 | 7.830522 | 9.377892 | 9.333861 | 10.038888 |
A vous de jouer!
Générez un data.frame nommée gene_stat_norm avec une ligne par gène à partir du tableau de données normalisées, avec les statistiques suivantes (une statistique par colonne):
## Gene-wise statistics after normalisation
message("Computing gene-wise statistics on log2-transformed and normalised counts")
gene_stat_norm <- data.frame(mean = apply(fa_expr_log2, 1, mean),
var = apply(fa_expr_log2, 1, var),
sd = apply(fa_expr_log2, 1, sd),
iqr = apply(fa_expr_log2, 1, IQR),
min = apply(fa_expr_log2, 1, min),
med = apply(fa_expr_log2, 1, median),
max = apply(fa_expr_log2, 1, max))
# Ajout du coefficient de variation
gene_stat_norm$coef_var <- (gene_stat_norm$sd / gene_stat_norm$mean)
kable(gene_stat_norm[100:109,], caption = "Statistiques par gène sur les données normalisées")| mean | var | sd | iqr | min | med | max | coef_var | |
|---|---|---|---|---|---|---|---|---|
| ENSMUSG00000000711 | 9.871818 | 0.5175315 | 0.7193966 | 0.3385336 | 9.222232 | 9.732185 | 12.335149 | 0.0728738 |
| ENSMUSG00000000724 | 5.626582 | 0.1705859 | 0.4130204 | 0.2736887 | 4.464461 | 5.727916 | 6.218882 | 0.0734052 |
| ENSMUSG00000000731 | 4.971927 | 0.5455394 | 0.7386064 | 1.1354431 | 3.683158 | 4.824454 | 6.203856 | 0.1485554 |
| ENSMUSG00000000732 | 5.590982 | 2.3882878 | 1.5454086 | 2.3926755 | 2.126857 | 5.661715 | 8.209062 | 0.2764109 |
| ENSMUSG00000000738 | 8.442163 | 0.2524164 | 0.5024106 | 0.4350922 | 7.455809 | 8.329340 | 9.540606 | 0.0595121 |
| ENSMUSG00000000740 | 10.803785 | 0.1537509 | 0.3921108 | 0.6194127 | 10.156658 | 10.757275 | 11.595716 | 0.0362938 |
| ENSMUSG00000000743 | 7.595448 | 1.0151685 | 1.0075557 | 0.7874255 | 6.135917 | 7.327031 | 10.469226 | 0.1326526 |
| ENSMUSG00000000751 | 7.397915 | 0.4442613 | 0.6665293 | 0.5886229 | 6.387679 | 7.283686 | 9.002489 | 0.0900969 |
| ENSMUSG00000000753 | 2.836140 | 2.9644420 | 1.7217555 | 1.5150652 | 0.000000 | 3.152627 | 5.192750 | 0.6070771 |
| ENSMUSG00000000759 | 8.601708 | 0.3046843 | 0.5519821 | 0.4823724 | 7.830522 | 8.491647 | 10.038888 | 0.0641712 |
Chaque gène étant donné par son identifiant dans la base de données ENSEMBL vous utiliserez le paquet biomaRt de bioconductor pour ajouter des annotations : symbole, chromosome, coordonnées génomiques, brin. Suivez pas à pas la méthode proposée (certaines étapes peuvent prendre quelques minutes):
chargez le paquet biomaRt, voire installer-le uniquement si nécessaire. Indiquez le code à l’emplacement adéquat dans le .Rmd.
sélectionnez la base de données ENSEMBL avec la fonction useMart(). Attention à choisir le bon génome avec l’agument dataset: “mmusculus_gene_ensembl”
avec la fonction getBM() récupérez de la base de données ENSEMBL les champs demandés (pour symbole utilisez external_gene_name) en appliquant “ensembl_geneid” pour l’agument filter et en indiquant pour l’argument values le vecteur des identifiants des gènes présents dans le dataframe gene_stat_norm. Vous obtenez un dataframe.
A présent, ajoutez au dataframe gene_stat_norm en 1ères colonnes les annotations retrouvées grâce à biomaRt. Attention, certains gènes ne sont pas retrouvés dans la version d’ENSEMBL sur biomaRt donc laissez des NA comme données manquantes dans ce cas. Nous vous recommandons d’utiliser la function merge() de R base ou bien left_join() de dplyr pour fusionner les deux dataframes en un seul.
### Gene annotations ####
message("Getting gene annotations")
# Connecting to Ensembl Biomart database and selecting mouse dataset
ensembl <- useMart("ensembl", dataset="mmusculus_gene_ensembl")
# Let's take a look a the names of our desired attributes in the dataset
kable(listAttributes(ensembl)[1:15,])| name | description | page |
|---|---|---|
| ensembl_gene_id | Gene stable ID | feature_page |
| ensembl_gene_id_version | Gene stable ID version | feature_page |
| ensembl_transcript_id | Transcript stable ID | feature_page |
| ensembl_transcript_id_version | Transcript stable ID version | feature_page |
| ensembl_peptide_id | Protein stable ID | feature_page |
| ensembl_peptide_id_version | Protein stable ID version | feature_page |
| ensembl_exon_id | Exon stable ID | feature_page |
| description | Gene description | feature_page |
| chromosome_name | Chromosome/scaffold name | feature_page |
| start_position | Gene start (bp) | feature_page |
| end_position | Gene end (bp) | feature_page |
| strand | Strand | feature_page |
| band | Karyotype band | feature_page |
| transcript_start | Transcript start (bp) | feature_page |
| transcript_end | Transcript end (bp) | feature_page |
# Let's look at the filters list to pick the desired filter
kable(listFilters(ensembl)[45:50,])| name | description | |
|---|---|---|
| 45 | with_uniprotsptrembl | With UniProtKB/TrEMBL ID(s) |
| 46 | with_wikigene | With WikiGene ID(s) |
| 47 | ensembl_gene_id | Gene stable ID(s) [e.g. ENSMUSG00000000001] |
| 48 | ensembl_gene_id_version | Gene stable ID(s) with version [e.g. ENSMUSG00000000001.5] |
| 49 | ensembl_transcript_id | Transcript stable ID(s) [e.g. ENSMUST00000000001] |
| 50 | ensembl_transcript_id_version | Transcript stable ID(s) with version [e.g. ENSMUST00000000001.5] |
## Collecting annotations
# I also recover the attribute "ensembl_gene_id" to be able to merge the dataframes later
getBM(attributes = c("ensembl_gene_id", "external_gene_name", "chromosome_name",
"start_position", "end_position", "strand"),
filter = "ensembl_gene_id",
values = row.names(gene_stat_norm),
mart = ensembl) -> annotations_df
## Merging the dataframes
# adding the column "ensembl_gene_id" to the gene_stat_norm dataframe for easier merging
gene_stat_norm$ensembl_gene_id <- row.names(gene_stat_norm)
# Merge with dplyr, although I use right_join not left_join
gene_stat_norm <- right_join(annotations_df, gene_stat_norm,
keep = FALSE)
# Number of genes for which no annotation could be recovered from Ensembl
length(gene_stat_norm[is.na(gene_stat_norm)])[1] 2745
Challenge falcultatif:
Réordonnez les gènes par position génomique et affichez les lignes 5 premières et 5 dernières lignes de ce tableau de statistiques.
#### Sorting genes by chromosome ####
message("Sorting genes by chromosome")
kable(gene_stat_norm[50:60, 1:6], caption = "Dataframe avant tri")| ensembl_gene_id | external_gene_name | chromosome_name | start_position | end_position | strand | |
|---|---|---|---|---|---|---|
| 50 | ENSMUSG00000000339 | Rtca | 3 | 116282612 | 116301857 | -1 |
| 51 | ENSMUSG00000000340 | Dbt | 3 | 116306719 | 116343630 | 1 |
| 52 | ENSMUSG00000000346 | Dazap2 | 15 | 100513230 | 100518642 | 1 |
| 53 | ENSMUSG00000000355 | Mcts1 | X | 37689455 | 37702303 | 1 |
| 54 | ENSMUSG00000000359 | Rem1 | 2 | 152468871 | 152477118 | 1 |
| 55 | ENSMUSG00000000374 | Trappc10 | 10 | 78022559 | 78080475 | -1 |
| 56 | ENSMUSG00000000378 | Ccm2 | 11 | 6496887 | 6546744 | 1 |
| 57 | ENSMUSG00000000384 | Tbrg4 | 11 | 6565598 | 6576067 | -1 |
| 58 | ENSMUSG00000000385 | Tmprss2 | 16 | 97365882 | 97412395 | -1 |
| 59 | ENSMUSG00000000386 | Mx1 | 16 | 97248235 | 97264107 | -1 |
| 60 | ENSMUSG00000000392 | Fap | 2 | 62331287 | 62404419 | -1 |
gene_stat_norm <- arrange(gene_stat_norm, chromosome_name, start_position)
kable(gene_stat_norm[50:60, 1:6], caption = "Dataframe après tri par position génomique.")| ensembl_gene_id | external_gene_name | chromosome_name | start_position | end_position | strand | |
|---|---|---|---|---|---|---|
| 50 | ENSMUSG00000056763 | Cspp1 | 1 | 10108212 | 10206993 | 1 |
| 51 | ENSMUSG00000102356 | 1700047N06Rik | 1 | 10127250 | 10128169 | -1 |
| 52 | ENSMUSG00000067851 | Arfgef1 | 1 | 10207796 | 10302895 | -1 |
| 53 | ENSMUSG00000102556 | Gm37569 | 1 | 10267306 | 10269027 | -1 |
| 54 | ENSMUSG00000042501 | Cpa6 | 1 | 10394945 | 10790170 | -1 |
| 55 | ENSMUSG00000103810 | Gm38005 | 1 | 10519470 | 10522214 | -1 |
| 56 | ENSMUSG00000083422 | Gm15604 | 1 | 10624323 | 10625778 | -1 |
| 57 | ENSMUSG00000103448 | Gm37133 | 1 | 10852681 | 10854602 | -1 |
| 58 | ENSMUSG00000048960 | Prex2 | 1 | 11063689 | 11373905 | 1 |
| 59 | ENSMUSG00000103494 | Gm37410 | 1 | 11434150 | 11437188 | -1 |
| 60 | ENSMUSG00000057715 | A830018L16Rik | 1 | 11484329 | 12046125 | 1 |
head(gene_stat_norm, 5) ensembl_gene_id external_gene_name chromosome_name start_position end_position strand mean var sd iqr min med max coef_var
1 ENSMUSG00000051951 Xkr4 1 3276124 3741721 -1 4.816674 1.3797317 1.1746198 2.0426500 3.050184 4.595623 6.681809 0.24386532
2 ENSMUSG00000103377 Gm37180 1 3435954 3438772 -1 2.314986 3.8640118 1.9657090 3.6903976 0.000000 1.867157 5.763218 0.84912350
3 ENSMUSG00000103161 Gm38148 1 3663115 3666126 -1 7.001132 0.2888639 0.5374606 0.3525436 6.205841 6.968086 8.836822 0.07676767
4 ENSMUSG00000102331 Gm19938 1 3717532 3729127 -1 4.758114 0.5164249 0.7186271 0.4096475 3.148896 4.705953 6.622021 0.15103193
5 ENSMUSG00000102592 Gm38385 1 3822233 3824583 1 5.857219 0.6559678 0.8099184 0.6477559 4.936802 5.660493 8.419961 0.13827695
tail(gene_stat_norm, 5) ensembl_gene_id external_gene_name chromosome_name start_position end_position strand mean var sd iqr min med max coef_var
20387 ENSMUSG00000108658 <NA> <NA> NA NA NA 4.938319 0.7136573 0.8447824 1.1299648 3.535143 4.911031 6.574707 0.17106681
20388 ENSMUSG00000108996 <NA> <NA> NA NA NA 3.364697 6.8214716 2.6117947 4.3200144 0.000000 3.717486 9.191275 0.77623478
20389 ENSMUSG00000109075 <NA> <NA> NA NA NA 1.239756 5.8909843 2.4271350 0.0000000 0.000000 0.000000 7.081730 1.95775172
20390 ENSMUSG00000109483 <NA> <NA> NA NA NA 3.707707 6.6925882 2.5870037 4.4430806 0.000000 4.943871 7.515378 0.69773683
20391 ENSMUSG00000109554 <NA> <NA> NA NA NA 6.866741 0.1589498 0.3986851 0.3635101 6.085779 6.869435 7.722308 0.05806032
par(mfrow=c(1,1))
hist(fa_expr_log2,
breaks = 100,
cex.axis = 0.7,
las = 1,
col = "skyblue",
xlab = "normalised counts",
main = "Distribution of all normalised counts")Distribution of all normalised counts
#### Box plots to show normalisation impact ####
# Raw data
boxplot(fa_expr_raw,
main = "Raw expression",
horizontal = TRUE,
col = fa_meta$color,
cex = 0.5,
cex.axis = 0.8,
las = 1)Boxplots observation of the normalisation procedures.
# With filtered expression and log2 transformation
boxplot(log2(fa_expr_filtered + 1),
main = "Filtered and log2 transformed expression",
horizontal = TRUE,
col = fa_meta$color,
cex = 0.5,
cex.axis = 0.8,
las = 1)Boxplots observation of the normalisation procedures.
# With filtered, standardised across samples and log2 transformation
boxplot(fa_expr_log2,
main = "Standardised and log2 transformed expression",
horizontal = TRUE,
col = fa_meta$color,
cex = 0.5,
cex.axis = 0.8,
las = 1)Boxplots observation of the normalisation procedures.
Avec les données d’expression brutes, les valeurs sont étalées avec quelques outliers extrêmement exprimés et une grande majorité de gènes pas ou peu exprimés.
Le filtrage de ces gènes peu ou pas exprimés et la transformation en log2 permet de “regrouper” les valeurs d’expression et de réduire l’impact des valeurs extrêmes dans l’analyse. C’est cependant une normalisation seulement intra-échantillon.
La standardisation sur le troisième quartile permet une normalisation inter-échantillons. Les médianes de tous les échantillons sont presque alignées. Cela permet notamment de considérablement réduire l’impact de la taille des librairies pour l’analyse entre échantillons. Par exemple le day2_2 dont l’expression moyenne semble nettement plus faible, retrouve des valeurs similaire aux autres échantillons.
A vous de jouer!
Pour réduire le nombre de gènes, nous allons écarter les gènes faiblement exprimés (log2 moyen inférieur à 4), et ne retenir que ceux qui montrent des variations importantes entre échantillons. Pour ce dernier critère, nous nous basons sur la variance.
Sélectionnez les gènes ayant un niveau log2 moyen minimal supérieur à 5 (\(m > 5\)) et une variance supérieure à 2 (\(s^2 > 2\)). Note: ces valeurs sont parfaitement arbitraires, elles ont été choisies pour obtenir un nombre raisonnable de gènes.
#### Selection of a subset of genes with igh expression and variance ####
message("Selecting genes with high expression and variance")
# Selecting high expression genes and with a high variance
most_expressed_genes_stat <- gene_stat_norm[(gene_stat_norm$mean > 5) & (gene_stat_norm$var > 2),]
# Recovering the desired genes from the normalised data
fa_expr_high_genes <- fa_expr_log2[most_expressed_genes_stat$ensembl_gene_id,]Dessinez des histogrammes des valeurs d’expression avant et après cette sélection de gènes, et commentez les différences.
#### Histograms of expression before and after gene selection ####
par(mfrow=c(1, 2))
hist(fa_expr_log2,
breaks = 100,
xlim = c(0, 25),
cex.main = 0.85,
cex.axis = 0.7,
las = 1,
col = "skyblue",
xlab = "normalised counts",
main = "Distribution of all genes
normalised counts")
hist(fa_expr_high_genes,
breaks = 100,
xlim = c(0, 25),
cex.main = 0.85,
cex.axis = 0.7,
las = 1,
col = "gold",
xlab = "normalised counts",
main = "Distribution of highly expressed genes
normalised counts")Distribution of normalised expression of all genes versus selected highly expressed genes.
On a beaucoup réduit le nombres de valeurs d’expression. Il reste cependant des valeurs nulles en proportion assez élevée. En selectionnant sur la base d’une variance forte, on a sans doute gardé des gènes qui ne sont pas du tout exprimés dans certaines conditions, et activés dans d’autres, probablement en réponse au traitement à l’acide folique.
De plus le sommet du pic de distribution sur les gènes sélectionnés est situé à une valeur de comptage normalisée similaire à celle pour tous les gènes, malgré le choix de gènes en moyenne les plus exprimés. Peut-être parce qu’une bonne partie des gènes très exprimés constants on été retirés par le filtre sur la variance, on garde ainsi des gènes avec des valeurs d’expression faibles dans certaines conditions qui viennent “compenser” les valeurs fortes dans d’autres.
Dessinez un box plot par échantillon des valeurs d’expression avant et après sélection des gènes, et commentez le résultat.
#### Boxplots of expression before and after gene selection ####
par(mfrow=c(1, 1))
# With all genes
boxplot(fa_expr_log2,
main = "Standardised and log2 transformed expression
all genes",
horizontal = TRUE,
col = fa_meta$color,
cex = 0.5,
cex.axis = 0.6,
las = 1)Boxplots of all genes versus selected genes normalised expression.
# With selected genes
boxplot(fa_expr_high_genes,
main = "Standardised and log2 transformed expression
selected genes",
horizontal = TRUE,
col = fa_meta$color,
cex = 0.5,
cex.axis = 0.6,
las = 1)Boxplots of all genes versus selected genes normalised expression.
Les différences de statistiques inter-échantillons sont à nouveaux plus élevées, les médianes sont plus écartées. L’échantillon day2_2 est encore assez différent des autres, avec des valeurs d’expression plus étalées.
Dessinez un plot ACP des échantillons en les colorant par condition avant et après normalisation.
#### PCA from raw counts, all genes ####
message("Running PCA with raw counts, all genes")
res_pca_raw <- PCA(t(fa_expr_raw), scale.unit = TRUE, graph = FALSE)
fviz_pca_ind(res_pca_raw,
col.ind = fa_meta$condition,
mean.point = FALSE,
title = "PCA on raw data, all genes")PCA on raw data, all genes
#### PCA from normalised counts, all genes ####
message("Running PCA with normalised counts, all genes")
res_pca_log2 <- PCA(t(fa_expr_log2), scale.unit = TRUE, graph = FALSE)
fviz_pca_ind(res_pca_log2,
col.ind = fa_meta$condition,
mean.point = FALSE,
title = "PCA on normalised data, all genes")PCA on normalised data, all genes
#### PCA from normalised counts, selected genes ####
message("Running PCA with normalised counts, selected genes")
res_pca_high_genes <- PCA(t(fa_expr_high_genes), scale.unit = TRUE, graph = FALSE)
fviz_pca_ind(res_pca_high_genes,
col.ind = fa_meta$condition,
mean.point = FALSE,
title = "PCA on normalised data, selected genes")PCA on normalised data, selected genes
La normalisation des données permet de rapprocher considérablement l’échantillon normal_2 et day1_1 qui semblaient être des outliers, de leurs réplicats biologiques. Elle permet également de rassembler les échantillons day2_1 et day2_3. La normalisation permet également de mieux séparer les conditions day3 et day7.
day2_2 semble être un outlier, quel que soit le traitement des données. Si on l’exclut, on observe dans l’ACP sur les gènes sélectionnés une claire séparation le long de la dimension 2 entre les échantillons après traitement à l’acide folique et les échantillons normaux. On observe également que les conditions day14 “redescendent” le long de cette dimension, probablement car l’effet de l’injection se résorbe. On peut même observer une disposition cyclique horaire partant de normal, passant aux temps récents après l’injection puis tendant à retourner vers les conditions normales.
dist()), coefficient de Pearson (cor(, method = "pearson")) et de Spearman (cor(, method = "spearman")).#### Sample distances ####
message("Computing inter-sample distances")
## Euclidian
# On transpose la matrice d'expression pour calculer la distance entre échantillons.
dist_euclidian <- dist(t(fa_expr_high_genes), method = "euclidean")
# Les distances de Pearson et Spearman sont calculées en soustrayant à 1 la corrélation de Pearson ou Spearman
## Pearson
dist_pearson <- as.dist(1 - cor(fa_expr_high_genes, use = "everything",
method = "pearson"))
## Spearman
dist_spearman <- as.dist(1 - cor(fa_expr_high_genes, use = "everything",
method = "spearman"))complete pour l’agglomération. Comparez les arbres d’échantillons obtenus avec ces trois métriques et choisissez celle qui vous paraît la plus pertinente.#### Sample clustering ####
message("Sample clustering")
tree_euclidian <- hclust(dist_euclidian, method = "complete")
tree_pearson <- hclust(dist_pearson, method = "complete")
tree_spearman <- hclust(dist_spearman, method = "complete")
par(bg = "darkgrey", mfrow=c(1, 1))
plotColoredClusters(tree_euclidian, labs = fa_meta$sampleName,
ylab = NA, xlab = NA, cex = 1, las = 1,
cols = fa_meta$color, col = "white",
main = "Samples Euclidian distance hierarchical clustering,
complete linkage")Dendograms samples clustering.
plotColoredClusters(tree_pearson, labs = fa_meta$sampleName,
ylab = NA, xlab = NA, cex = 1, las = 1,
cols = fa_meta$color, col = "white",
main = "Samples Pearson distance hierarchical clustering,
complete linkage")Dendograms samples clustering.
plotColoredClusters(tree_spearman, labs = fa_meta$sampleName,
ylab = NA, xlab = NA, cex = 1, las = 1,
cols = fa_meta$color, col = "white",
main = "Samples Spearman distance hierarchical clustering,
complete linkage")Dendograms samples clustering.
Le clustering basé sur la distance euclidienne sépare complètement l’échantillon day2_2 des autres, il est traité comme un outlier très marqué. Les échantillons normaux sont groupés entre eux et séparé d’un cluster qui englobe tous les échantillons traités à part le day2_2. Ce cluster d’échantillons traités est séparé en un groupe de prélèvements peu après le traitement à l’acide folique (day1, day2, day3) et un autre avec des temps plus longs (day7 et day14).
Les clusterings basés sur les distances de Spearman et Pearson donnent des résultats très proches. Deux super clusters dont un comprenant les échantillons prélevés peu après le traitement, à l’intérieur duquel les différentes temps sont bien regroupés (mis à part le day2_2), et un regroupant les conditions sans traitement et les prélèvement plus tardifs.
Les trois clusterings donnent des résultats qui me semblent cohérents, cependant l’excusion nette du day2_2 des autres conditions par la distance euclidienne me parait justifiée (au regard de la PCA) ainsi que la séparation plus franche des conditions normales et des conditions traitées.
Je décide d’utiliser le clustering hierarchique des échantillons basé sur la distance euclidienne.
#### Gene tree with Pearson correlation ####
message("Drawing gene tree")
# Pearson distance gene
dist_gene_pearson <- as.dist(1 - cor(t(fa_expr_high_genes),
use = "everything", method = "pearson"))
# tree
tree_gene_pearson <- hclust(dist_gene_pearson, method = "complete")#### Plot the gene tree with boxes to denote the clusters ####
message("Plotting gene tree")
par(bg = "white")
plot(tree_gene_pearson, las = 1,
ylab = NA, xlab = NA, cex = 0.1, hang = -1, col = "grey",
main = "Genes Pearson distance hierarchical clustering,
complete linkage")
# Estimating an appropriate number of clusters
abline(h=1.7, col= "red")# Let's try cutting the tree into 6 clusters at the height 1.7
clusters_gene_pearson <- cutree(tree_gene_pearson, h=1.7)
# How many genes are in each cluster?
table(clusters_gene_pearson)clusters_gene_pearson
1 2 3 4 5
321 122 102 111 34
# Saving clustering results in gene dataframe
most_expressed_genes_stat$cluster <- clusters_gene_pearson
# We add the cluster information in the most_expressed_genes_stat
my_colors = c("orange", "blue", "darkred",
"skyblue", "darkgreen")
plot(tree_gene_pearson, las = 1,
ylab = NA, xlab = NA, cex = 0.1, hang = -1, col = "grey",
main = "Genes Pearson distance hierarchical clustering,
complete linkage")
rect.hclust(tree_gene_pearson, h=1.7, border = my_colors)L’arbre comporte une séparation en deux cluters principaux qui se subdivisent ensuite rapidement. Le choix du nombre de clusters n’est pas évident, une séparation en 5 ou 6 clusters me paraitrait raisonnable. En coupant l’arbre à une hauteur de 1.7, j’opte pour 5 clusters.
#### Heatmap with biclustering ####
message("heatmap with biclustering")
annot_clust = data.frame(as.factor(most_expressed_genes_stat$cluster))
colnames(annot_clust) <- "cluster"
rownames(annot_clust) <- rownames(fa_expr_high_genes)
pheatmap(fa_expr_high_genes,
cluster_rows = TRUE,
cluster_cols = TRUE,
clustering_distance_rows = dist_gene_pearson,
clustering_distance_cols = dist_euclidian,
border_color = NA,
show_rownames = FALSE,
annotation_row = annot_clust,
annotation_names_row = FALSE,
cutree_rows = 5,
cutree_cols = 5,
scale = "row",
use_raster = TRUE,
angle_col = "45",
main = "Biclustering of genes (Pearson distance)
and samples (euclidian distance)")Biclustering of genes and samples.
#### Heatmap with clustering on genes only ####
message("Heatmap with gene clustering")
pheatmap(fa_expr_high_genes,
cluster_rows = TRUE,
cluster_cols = FALSE,
clustering_distance_rows = dist_gene_pearson,
border_color = NA,
show_rownames = FALSE,
legend = FALSE,
annotation_row = annot_clust,
annotation_names_row = FALSE,
cutree_rows = 5,
scale = "row",
use_raster = TRUE,
angle_col = "45",
main = "Heatmap with clustering of genes only
(Pearson distance)")Heatmap with clustering of genes (Pearson distance)
Interprétez les résultats en quelques phrases.
On observe avec ou sans clustering des échantillons le profil atypique de l’échantillon day2_2. À mon sens, cela confirme le choix de la distance euclidienne pour le clustering des échantillons. Cet échantillon surexprime nettement le cluster de gènes 3 et sous-exprime le cluster 1.
Ensuite, on observe que le cluster de gènes 2 est sous-exprimé dans les condions normales par rapport aux conditions traitées. Ces gènes sont possiblement impliqués dans la réponse au traitement à l’acide folique et à l’atteinte aux reins qu’il cause. Le niveau d’expression du cluster 2 redescend dans les day14.
Les clusters de gènes 1 et 4 sont globalement moins exprimés dans les condions peu après le traitement. Leurs niveaux d’expression remontent dans les conditions plus tardives.
A vous de jouer!
Effectuez une analyse d’enrichissement fonctionnel avec les principaux clusters obtenus dans la section précédente.
#### Run enrichment analysis with gost() ####
message("Running enrichment analysis for the selected genes")
gene_list <- most_expressed_genes_stat$ensembl_gene_id
gost_res <- gost(query = gene_list,
organism = "mmusculus",
significant = TRUE,
user_threshold = 0.05, # custom p-value threshold for significance
correction_method = "fdr",
domain_scope = "annotated",
as_short_link = FALSE)
gostplot(gost_res, capped = TRUE,
interactive = TRUE)Fonctional enrichment analysis
Je comprends mal la différence entre les énoncés de la question 5 et du challenge facultatif. Je suppose que dans la question obligatoire il est demandé une analyse d’enrichissement globale sur tous les gènes les plus exprimés et variables et que les clusters de gènes n’interviennent que dans la section facultative…
Challenge falcultatif:
Effectuez une analyse d’enrichissement sur chacun des clusters obtenus à partir de l’arbre des gènes.
#### Enrichment by cluster ####
message("Enrichment analysis by clusters")
## Extracting list of genes by cluster
cluster1 <- most_expressed_genes_stat[most_expressed_genes_stat$cluster == 1, "ensembl_gene_id"]
cluster2 <- most_expressed_genes_stat[most_expressed_genes_stat$cluster == 2, "ensembl_gene_id"]
cluster3 <- most_expressed_genes_stat[most_expressed_genes_stat$cluster == 3, "ensembl_gene_id"]
cluster4 <- most_expressed_genes_stat[most_expressed_genes_stat$cluster == 4, "ensembl_gene_id"]
cluster5 <- most_expressed_genes_stat[most_expressed_genes_stat$cluster == 5, "ensembl_gene_id"]
list_clusters_genes = list(cluster1, cluster2, cluster3,
cluster4, cluster5)
gost_clusters_res <- gost(query = list_clusters_genes,
organism = "mmusculus",
significant = TRUE,
multi_query = TRUE,
user_threshold = 0.05, # custom p-value threshold for significance
correction_method = "fdr",
domain_scope = "annotated",
as_short_link = FALSE)
gostplot(gost_clusters_res, capped = TRUE,
interactive = TRUE)Fonctional enrichment analysis, by cluster
Quelques observations :
-Les gènes du cluster 2, exprimés dans les conditions peu après le traitment à l’acide folique, sont souvent impliqués dans la réponse immunitaire et l’inflamation, probablement pour répondre au choc produit par le traitement sur les reins de la souris.
-Le cluster 4 (dont l’expression baisse dans les conditions peu après le traitement avant de remonter) comprend des gènes impliqués dans le transport transmembranaire, la glycolyse… Il reflète probablement l’activité normale des reins.
-Les gènes du cluster 3 (activé dans l’échantillons outlier day2_2) semblent impliqués dans la division cellulaire…
Résumez en quelques phrases vos conclusions à partir des résultats obtenus.
Dans l’analyse d’enrichissement générale on semble retrouver de nombreux gènes impliqués dans la réponse immunitaire.
L’analyse par cluster tend à suggérer que ces gènes sont notamment activés en réponse au traitement à l’acide folique dans les jours 1, 2, 3 et 7. Leur expression rediminue ensuite au jour 14.
Les gènes reflétant l’activité normale du rein sont au contraire moins exprimés juste après l’injection d’acide folique (jours 1 et 2) puis leur expression commence à remonter après le jour 3.
En conclusion, on observe ici la certaine reversibilité du traitement à l’acide folique sur les souris. Les reins retrouvent une expression génétique proche de la normale. Cependant, si l’on regarde les heatmaps, on observe une sous-partie du cluster de gènes 1 dont l’expression est plus haute que la normale aux jours 7 et 14, peut-être que ceux ci refletent des séquelles plus durables du traitement sur le rein ?
#### Session info ####
sessionInfo()R version 4.0.3 (2020-10-10)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)
Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.0.3/lib/libopenblasp-r0.3.10.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] biomaRt_2.46.3 ClassDiscovery_3.3.13 oompaBase_3.2.9 cluster_2.1.1 dplyr_1.0.6 pheatmap_1.0.12 gprofiler2_0.2.0 factoextra_1.0.7 ggplot2_3.3.3 FactoMineR_2.4 knitr_1.33
loaded via a namespace (and not attached):
[1] colorspace_2.0-1 ggsignif_0.6.1 ellipsis_0.3.2 rio_0.5.26 mclust_5.4.7 ggpubr_0.4.0 farver_2.1.0 ggrepel_0.9.1 DT_0.17 bit64_4.0.5 AnnotationDbi_1.52.0 fansi_0.4.2 xml2_1.3.2 leaps_3.1
[15] cachem_1.0.5 jsonlite_1.7.2 broom_0.7.5 dbplyr_2.1.1 shiny_1.6.0 compiler_4.0.3 httr_1.4.2 backports_1.2.1 assertthat_0.2.1 fastmap_1.1.0 lazyeval_0.2.2 later_1.2.0 htmltools_0.5.1.1 prettyunits_1.1.1
[29] tools_4.0.3 gtable_0.3.0 glue_1.4.2 rappdirs_0.3.3 Rcpp_1.0.6 carData_3.0-4 Biobase_2.50.0 cellranger_1.1.0 jquerylib_0.1.4 vctrs_0.3.8 crosstalk_1.1.1 xfun_0.23 stringr_1.4.0 openxlsx_4.2.3
[43] mime_0.10 lifecycle_1.0.0 rstatix_0.7.0 XML_3.99-0.6 MASS_7.3-53.1 scales_1.1.1 hms_1.1.0 promises_1.2.0.1 parallel_4.0.3 RColorBrewer_1.1-2 yaml_2.2.1 curl_4.3.1 memoise_2.0.0 sass_0.4.0
[57] stringi_1.6.2 RSQLite_2.2.7 highr_0.9 S4Vectors_0.28.1 BiocGenerics_0.36.1 zip_2.1.1 rlang_0.4.11 pkgconfig_2.0.3 bitops_1.0-7 evaluate_0.14 lattice_0.20-41 purrr_0.3.4 htmlwidgets_1.5.3 labeling_0.4.2
[71] bit_4.0.4 tidyselect_1.1.1 magrittr_2.0.1 R6_2.5.0 IRanges_2.24.1 generics_0.1.0 oompaData_3.1.1 DBI_1.1.1 pillar_1.6.1 haven_2.3.1 foreign_0.8-81 withr_2.4.2 scatterplot3d_0.3-41 abind_1.4-5
[85] RCurl_1.98-1.3 tibble_3.1.2 crayon_1.4.1 car_3.0-10 utf8_1.2.1 BiocFileCache_1.14.0 plotly_4.9.3 rmarkdown_2.8 progress_1.2.2 grid_4.0.3 readxl_1.3.1 data.table_1.14.0 blob_1.2.1 forcats_0.5.1
[99] digest_0.6.27 flashClust_1.01-2 xtable_1.8-4 tidyr_1.1.3 httpuv_1.6.1 openssl_1.4.4 stats4_4.0.3 munsell_0.5.0 viridisLite_0.4.0 bslib_0.2.5.1 askpass_1.1